import threading
import Queue
import time

class TraceObject():
    def __init__(self):
        self.brith_time = time.localtime()
        self.id = time.time()

class Task(TraceObject):
    def Run(self):
        return None
        
class Executor:
    def Exec(self, task):
        return None    

class Observer:
    def OnNotify(self, task, result):
        return None




class ObserverModel:
    def __init__(self, observer_list = None):
        self.observer_list = observer_list
        if self.observer_list == None:
            self.observer_list = []
        
    def AddObserver(self, observer):
        self.observer_list.append(observer)
        
    def RemoveobServer(self, observer):
        self.observer_list.remove(observer)
    
    
    def NotifyObservers(self, task, result = None):
        for observer in self.observer_list:
            self.Notify(observer, task, result)
        
    '''Reimplement this if need'''
    def Notify(self, observer, task, result):
        observer.OnNotify(task, result)
     
    def GetObserverList(self):
        return self.observer_list
    


class TaskQueueModel:
    def __init__(self, executor = None, task_queue = None):
        self.task_queue = task_queue
        self.executor = executor
        if self.task_queue == None:
            self.task_queue = Queue.Queue()
    
    def GetTaskQuque(self):  
        return self.task_queue
          
    def IsEmpty(self):
        return self.task_queue.empty()
    
    def AddTask(self, task):
        self.task_queue.put(task)
    
    def AddTasks(self, task_list):
        for task in task_list:
            self.AddTask(task)
    
    def Get(self):
        return self.task_queue.get()
    
    def GetTaskCount(self):
        return self.task_queue.qsize()
    
    def Process(self):
        task = self.task_queue.get()
        result = self.HandleTask(task)
        return (task, result)

    def HandleTask(self, task):
        if self.executor == None:
            return task.Run()
        else:
            return self.executor.Exec(task)



class LoopThreadModel(threading.Thread):        
    def __init__(self):
        threading.Thread.__init__(self)
        self.work_event = threading.Event()
        self.bWork = False

    def __del__(self):
        self.Stop()
    
    def Start(self):
        self.bWork = True
        self.start()
    
    def Stop(self):
        self.bWork = False
    
    def HasWork(self):
        return False

    def DoWork(self):
        pass
    
    def WorkDone(self, task, result):
        pass
    
    def Active(self):
        self.work_event.set()

    def Sleep(self):
        self.work_event.clear()
        self.work_event.wait()

    def IsBusy(self):
        return self.work_event.isSet()
    

    def run(self):
        while(self.bWork):
            if not self.HasWork():
                self.Sleep()
            task, result = self.DoWork()
            if not(task == None) or not(result == None):
                self.WorkDone(task, result)



class TaskHandleThread(LoopThreadModel, TaskQueueModel):        
    def __init__(self, executor = None, task_queue = None):
        LoopThreadModel.__init__(self)
        TaskQueueModel.__init__(self, executor, task_queue)
        
    def PostTask(self, task):
        self.AddTask(task)
        self.Active()
        
    def HasWork(self):
        return not self.IsEmpty()
        
    def DoWork(self):
        task, result = self.Process()
        return (task, result)



class TaskHandleNotiyThread(TaskHandleThread, ObserverModel):
    def __init__(self, executor = None, task_queue = None, observer_list = None):
        TaskHandleThread.__init__(self, executor, task_queue)
        ObserverModel.__init__(self, observer_list)
    
    def WorkDone(self, task, result):
        self.NotifyObservers(task, result)




class DispatchModel(LoopThreadModel):
    MAX_THREAD_COUNT = 100
    def __init__(self, task_queue, max_thread_count = MAX_THREAD_COUNT):
        LoopThreadModel.__init__(self)
        self.task_queue = task_queue
        self.max_thread_count = max_thread_count
        self.handle_thread_list = []
    
    def CreateWorkThread(self):
        pass

    def SetMaxThreadCount(self, max_count):
        self.max_thread_count = max_count
        
    def Schedule(self):
        self.Active()
        
    def DoWork(self):
        task, thread = self.__Dispatch()
        return (task, thread)
    
    def __Dispatch(self):
        while not self.task_queue.empty():
            thread = None
            if len(self.handle_thread_list) == 0:
                thread = self.__Fork()
            else:
                index, min_count= self.__GetMinQueueIndex()
                thread = self.handle_thread_list[index]
                if (min_count > 1 and len(self.handle_thread_list) <= self.max_thread_count):
                    thread = self.__Fork()

            task = self.task_queue.get()
            thread.PostTask(task)
            return (task, thread)

    def __Fork(self):
        thread = self.CreateWorkThread()
        self.handle_thread_list.append(thread)
        thread.Start()
        return thread

    def __GetMinQueueIndex(self):
        index = 0
        min_count = self.handle_thread_list[0].GetTaskCount()
        for i in range(0, len(self.handle_thread_list) - 1):
            if self.handle_thread_list[i].GetTaskCount() < min_count:
                min_count = self.handle_thread_list[i].GetTaskCount()
                index = i
        return (index, min_count)

class TaskDispatcher(DispatchModel):
    class Delegate:
        def CreateExecutor(self):
            pass

    def __init__(self, task_queue, observer_list, delegate):
        DispatchModel.__init__(self, task_queue)
        self.observer_list = observer_list
        self.delegate = delegate
        
    def CreateWorkThread(self):
        thread = TaskHandleNotiyThread(self.delegate.CreateExecutor())
        for observer in self.observer_list:
            thread.AddObserver(observer)
        return thread
    

class NotifyTask(Task):
    def __init__(self, delegate, task, result):
        Task.__init__(self)
        self.result = result
        self.task = task
        self.delegate = delegate
        
    def Run(self):
        self.delegate.OnTaskComplete(self.task, self.result)  


class TaskNotifyer(TaskHandleThread, Observer):
    class Delegate:
        def OnTaskComplete(self, task, result):
            pass

    def __init__(self, delegate):
        TaskHandleThread.__init__(self)
        self.delegate = delegate
        
    def OnNotify(self, task, result):
        self.PostTask(NotifyTask(self.delegate, task, result))



class SyncTask(Task):
    def __init__(self, task):
        self.event = threading.Event()
        self.task = task
        self.result = None
        
class SyncTaskMgr():
    def __init__(self):
        self.map = {}
    
    def FindSyncTask(self, task):
        return self.map.has_key(task.id)
    
    def CompleteSyncTask(self, task, result):
        self.map[task.id].result = result;
        self.map[task.id].event.set()
        
    def WaitSyncTaskResult(self, task):
        self.map[task.id].event.wait()
        result = self.map[task.id].result
        self.__RemoveSyncTask(task)
        return result

    def AddSyncTask(self, task):
        self.map[task.id] = SyncTask(task)
        
    def __RemoveSyncTask(self, task):
        del self.map[task.id]
        
class TaskModel(TaskQueueModel, ObserverModel, TaskDispatcher.Delegate, TaskNotifyer.Delegate):
    def __init__(self):
        TaskQueueModel.__init__(self)
        ObserverModel.__init__(self)
        self.notifyer = TaskNotifyer(self)
        list = [self.notifyer]
        self.dispatcher = TaskDispatcher(self.GetTaskQuque(), list, self)
        self.sync_mgr = SyncTaskMgr()
        self.bStart = False

    def Start(self):
        if (self.bStart):
            return
        self.dispatcher.Start()
        self.notifyer.Start()
        self.bStart = True
    
    def PostTask(self, task):
        self.AddTask(task)
        self.dispatcher.Schedule()
        
    def SendTask(self, task):
        self.sync_mgr.AddSyncTask(task)
        self.PostTask(task)
        return self.sync_mgr.WaitSyncTaskResult(task)
        
        
        
    '''Interface'''
    def CreateExecutor(self):
        return None
    
    '''Reimplement this if need'''
    def OnTaskComplete(self, task, result):
        self.NotifyObservers(task, result)
        if (self.sync_mgr.FindSyncTask(task)):
            self.sync_mgr.CompleteSyncTask(task, result)




#+--------------------------Test---------------------------+#

class PrintObserver():
    def OnPrintComplete(self, string):
        print string

class PrintTask(Task):
    def __init__(self):
        Task.__init__(self)
        self.str = "String"
        
    def Run(self):
        print "From Run"
        return self.str + " Return By Run"

class Printer(Executor):
    def Exec(self, task):
        print "From Exec"
        return task.str + " Return By Exec"

class PrintProcesser_Executor(TaskModel):
    def CreateExecutor(self):
        return Printer()
    
    def Notify(self, observer, task, result):
        observer.OnPrintComplete(result)

    
class PrintProcesser_Tasker(TaskModel):
    def CreateExecutor(self):
        return None
    
    def Notify(self, observer, task, result):
        observer.OnPrintComplete(result)
        



if __name__ == '__main__':
    ob = PrintObserver()
    processer = PrintProcesser_Executor()
    processer.AddObserver(ob)
    processer.Start()
    processer.PostTask(PrintTask())
    result = processer.SendTask(PrintTask())
    print "SyncTask Result", result
    
    #processer = PrintProcesser_Tasker()
    #processer.AddObserver(ob)
    #processer.Start()
    #processer.PostTask(PrintTask())

